package com.cxyd.modules.wcs.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.cxyd.common.constant.RcsConstants;
import com.cxyd.common.constant.WcsConstants;
import com.cxyd.common.page.PageData;
import com.cxyd.common.service.impl.CrudServiceImpl;
import com.cxyd.common.utils.ApiResult;
import com.cxyd.common.utils.ConvertUtils;
import com.cxyd.modules.api.dto.CancelTaskDTO;
import com.cxyd.modules.log.service.SysLogWorkService;
import com.cxyd.modules.wcs.dao.ReceiveTaskDao;
import com.cxyd.modules.wcs.dto.ReceiveTaskDTO;
import com.cxyd.modules.wcs.dto.TrkDTO;
import com.cxyd.modules.wcs.entity.*;
import com.cxyd.modules.wcs.service.*;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.Duration;
import java.time.Instant;
import java.util.Date;
import java.util.Map;
import java.util.UUID;

/**
 * 接收指令信息
 *
 * @author Cycle gengruyi01@163.com
 * @since 1.0.0 2022-11-14
 */
@Service
public class ReceiveTaskServiceImpl extends CrudServiceImpl<ReceiveTaskDao, ReceiveTaskEntity, ReceiveTaskDTO> implements ReceiveTaskService {
    @Resource
    private TrkService trkService;
    @Resource
    private AgvTrkHkService hkService;
    @Resource
    private StnMappingService stnMappingService;
    @Resource
    private SysLogWorkService logWorkService;

    /**
     * 搜索框条件拼接
     */
    @Override
    public QueryWrapper<ReceiveTaskEntity> getWrapper(Map<String, Object> params) {
        String requestPk = (String) params.get("requestPk");
        String contNo = (String) params.get("contNo");
        String frmPos = (String) params.get("frmPos");
        String toPos = (String) params.get("toPos");
        String status = (String) params.get("status");
        String startDate = (String) params.get("startDate");
        String endDate = (String) params.get("endDate");

        QueryWrapper<ReceiveTaskEntity> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.isNotBlank(requestPk), "request_pk", requestPk);
        wrapper.eq(StringUtils.isNotBlank(status), "status", status);
        wrapper.eq(StringUtils.isNotBlank(contNo), "cont_no", contNo);
        wrapper.like(StringUtils.isNotBlank(frmPos), "frm_pos", frmPos);
        wrapper.like(StringUtils.isNotBlank(toPos), "to_pos", toPos);

        wrapper.ge(StringUtils.isNotBlank(startDate), "req_time", startDate);
        wrapper.le(StringUtils.isNotBlank(endDate), "req_time", endDate);

        return wrapper;
    }

    @Override
    public ReceiveTaskDTO getReceiveTask() {
//        1.入库任务优先解析
        QueryWrapper<ReceiveTaskEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(ReceiveTaskEntity::getStatus, 0)
                .isNotNull(ReceiveTaskEntity::getContNo)
                .orderBy(true, true, ReceiveTaskEntity::getUpdateDate)
                .last("limit 1");
        ReceiveTaskEntity entity = baseDao.selectOne(qw);
        return ConvertUtils.sourceToTarget(entity, ReceiveTaskDTO.class);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ApiResult receiveCheckHandler(ReceiveTaskDTO dto) {
        String workName = "[流程-RECEIVE]接收指令";
        String workProc = "ReceiveTaskServiceImpl.receiveCheckHandler";
        String code = WcsConstants.PROC_CODE_SUCCESS;
        String msg = "接收指令成功!";
//        0.基础数据校验
        if (StringUtils.isBlank(dto.getRequestPk())) {
            return new ApiResult().feedBack("接收指令失败!请求主键不能为空!", dto.getRequestPk());
        }
        if (StringUtils.isBlank(dto.getContNo())) {
            return new ApiResult().feedBack("接收指令失败!料架码不能为空!", dto.getRequestPk());
        }
        if (StringUtils.isBlank(dto.getContType())) {
            return new ApiResult().feedBack("接收指令失败!料架类别不能为空!", dto.getRequestPk());
        }
        if (StringUtils.isBlank(dto.getIsFull())) {
            return new ApiResult().feedBack("接收指令失败!料架空满不能为空!", dto.getRequestPk());
        }
        if (StringUtils.isBlank(dto.getFrmPos())) {
            return new ApiResult().feedBack("接收指令失败!起始点不能为空!", dto.getRequestPk());
        }
        if (StringUtils.isBlank(dto.getToPos())) {
            return new ApiResult().feedBack("接收指令失败!目标机台不能为空!", dto.getRequestPk());
        }
//        1.校验PK是否重复下发
        QueryWrapper<ReceiveTaskEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(ReceiveTaskEntity::getRequestPk, dto.getRequestPk())
                .or().eq(ReceiveTaskEntity::getToPos, dto.getToPos()).lt(ReceiveTaskEntity::getStatus, 2)
                .last("limit 1");
        ReceiveTaskEntity entity = baseDao.selectOne(qw);
        if (entity != null) {
            code = "receive_error_001";
            msg = "PK任务已存在或目标机台已存在搬送任务!请核查后重试!";
            logWorkService.workLogging(workName, dto.getContNo(), code, msg, "", workProc, WcsConstants.MES_USER_NAME, 0);
            return new ApiResult().feedBack(msg, dto.getRequestPk());
        }
//        加个标签,记录前置任务与后置任务的关联：TT=都能做  TF=前置可,后置不可 FT=前置不可,后置可 FF=都不可
        String tag = "TT";
//        2.校验任务是否能齐套完成(前置任务与后置任务)
        StnMappingEntity usableStock = stnMappingService.getUsableStnByType(dto.getContType(), dto.getIsFull());
        if (usableStock == null) {
//                暂无可用缓存货位（后置任务无法做到）
            tag = "TF";
//            return new ApiResult().feedBack("缓存区暂无所需料架!请核查后重试!", dto.getRequestPk());
        }
//        前后任务处理
        StnMappingEntity machineStn = stnMappingService.getUsableStnByNo(dto.getToPos(), 2);
        if (machineStn != null) {
            StnMappingEntity usableStn = stnMappingService.getUsableStnByNo(WcsConstants.CACHE_POSITION, 0);
            if (usableStn == null) {
//                前置任务无法完成
                tag = "TT".equals(tag) ? "FT" : "FF";
//                return new ApiResult().feedBack("缓存区暂无空闲位置可存放从机台返回的料架!请核查后重试!", dto.getRequestPk());
            }
//            如果前置任务可行且后置任务不可行再生成MCS内部任务
            if ("TF".equals(tag)) {
                //                更新站点状态
                usableStn.setStatus(1);
                usableStn.setContNo(machineStn.getContNo());
                usableStn.setUpdateDate(new Date());
                stnMappingService.updateById(usableStn);
//                此处生产MCS内部搬送任务（前置任务）
                TrkDTO mcsTrk = new TrkDTO();
                mcsTrk.setRequestPk(dto.getRequestPk() + "#mcs");
                mcsTrk.setContNo(machineStn.getContNo());
                mcsTrk.setDvcNo("HK_AGV");
                mcsTrk.setContType(machineStn.getContType());
                mcsTrk.setFrmPos(machineStn.getWcsStation());
                mcsTrk.setToPos(usableStn.getWcsStation());
                mcsTrk.setCurPos(machineStn.getWcsStation());
                mcsTrk.setTrkType(WcsConstants.TRK_TYPE_IN);
//                前置任务的空满与MES所需料架的空满相反
                mcsTrk.setIsFull("false".equals(dto.getIsFull()) ? "true" : "false");
                mcsTrk.setCreator(WcsConstants.WCS_USER_ID);
                mcsTrk.setCreateDate(new Date());
                mcsTrk.setUpdater(WcsConstants.WCS_USER_ID);
                trkService.save(mcsTrk);
//                任务生产成功,预释放机台状态
                machineStn.setStatus(1);
                machineStn.setComments("MCS内部生成[前置任务]搬送!");
                machineStn.setUpdateDate(new Date());
                stnMappingService.updateById(machineStn);
            }
        }
//        根据tag反馈MES作业情况
        if ("TT".equals(tag)) {
            return new ApiResult().feedBack(dto.getRequestPk());
        } else if ("TF".equals(tag)) {
            code = "receive_error_002";
            msg = "无所需料架Type[" + dto.getContType() + "]+isFull[" + dto.getIsFull() + "]导致[后置任务]无法搬送!请核查后重试!";
            logWorkService.workLogging(workName, dto.getContNo(), code, msg, "", workProc, WcsConstants.MES_USER_NAME, 0);
            return new ApiResult().feedBack(msg, dto.getRequestPk());
        } else if ("FT".equals(tag)) {
            code = "receive_error_003";
            msg = "无空闲缓存区货位导致[前置任务]无法搬送!请核查后重试!";
            logWorkService.workLogging(workName, dto.getContNo(), code, msg, "", workProc, WcsConstants.MES_USER_NAME, 0);
            return new ApiResult().feedBack(msg, dto.getRequestPk());
        } else {
            code = "receive_error_004";
            msg = "既无所需料架Type[" + dto.getContType() + "]+isFull[" + dto.getIsFull() + "],也无空闲缓存区货位,[前置任务]&[后置任务]均无法搬送!请核查后重试!";
            logWorkService.workLogging(workName, dto.getContNo(), code, msg, "", workProc, WcsConstants.MES_USER_NAME, 0);
            return new ApiResult().feedBack(msg, dto.getRequestPk());
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ApiResult cancelCheckHandler(CancelTaskDTO dto) {
        String workName = "[流程-CANCEL]指令取消";
        String workProc = "ReceiveTaskServiceImpl.cancelCheckHandler";
        String code = WcsConstants.PROC_CODE_SUCCESS;
        String msg = "指令取消成功!";
        if (StringUtils.isBlank(dto.getRequestPk())) {
            return new ApiResult().feedBack("指令取消失败!请求主键不能为空!", dto.getRequestPk());
        }
//        判断是否存在对应任务
        QueryWrapper<ReceiveTaskEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(ReceiveTaskEntity::getRequestPk, dto.getRequestPk());
        ReceiveTaskEntity entity = baseDao.selectOne(qw);
        if (entity == null) {
            return new ApiResult().feedBack("指令取消失败![" + dto.getRequestPk() + "]任务不存在!", dto.getRequestPk());
        }
        if (entity.getStatus() > 2) {
            code = "cancel_error_001";
            msg = "指令取消失败![" + dto.getRequestPk() + "]任务无需再次取消!";
            logWorkService.workLogging(workName, dto.getRequestPk(), code, msg, "", workProc, WcsConstants.MES_USER_NAME, 3L);
            return new ApiResult().feedBack(msg, dto.getRequestPk());
        }
//        判断任务是否生成(如果生成一并取消)
        TrkEntity trkEntity = trkService.selectByRequestPk(dto.getRequestPk());
        if (trkEntity == null) {
            entity.setStatus(WcsConstants.RECEIVE_TASK_CANCEL);
            entity.setComments("指令取消成功-MES!");
            entity.setUpdateDate(new Date());
            baseDao.updateById(entity);
            return new ApiResult().feedBack(dto.getRequestPk());
        }
//        判断任务是否执行
        if (trkEntity.getTrkSt() == 0) {
            trkEntity.setComments("任务状态为[" + trkEntity.getTrkSt() + "]时,MES通知取消搬送!");
            trkEntity.setTrkSt(WcsConstants.TRK_STATUS_CANCEL);
            trkEntity.setUpdater(WcsConstants.MES_USER_ID);
            trkEntity.setUpdateDate(new Date());
            trkService.updateById(trkEntity);

            entity.setStatus(WcsConstants.RECEIVE_TASK_CANCEL);
            entity.setComments("指令取消成功-MES!");
            entity.setUpdateDate(new Date());
            baseDao.updateById(entity);
            return new ApiResult().feedBack(dto.getRequestPk());
        }
//        判断小车任务执行到哪一步
        AgvTrkHkEntity agvEntity = hkService.getAgvTrkByTrkId(trkEntity.getTrkId());
        if (agvEntity != null && agvEntity.getStatus() > 0) {
            code = "cancel_error_002";
            msg = "指令取消失败!小车搬送途中!任务无法取消";
            logWorkService.workLogging(workName, dto.getRequestPk(), code, msg, "", workProc, WcsConstants.MES_USER_NAME, 3L);
            return new ApiResult().feedBack(msg, dto.getRequestPk());
        }
//        记录日志
        agvEntity.setStatus(RcsConstants.AGV_TRK_HK_CANCEL);
        agvEntity.setData("搬送状态为[" + agvEntity.getStatus() + "]时!MES通知取消搬送!");
        agvEntity.setUpdater(WcsConstants.MES_USER_ID);
        agvEntity.setUpdateDate(new Date());
        hkService.updateById(agvEntity);

        trkEntity.setComments("任务状态为[" + trkEntity.getTrkSt() + "]时,MES通知取消搬送!");
        trkEntity.setTrkSt(WcsConstants.TRK_STATUS_CANCEL);
        trkEntity.setUpdater(WcsConstants.MES_USER_ID);
        trkEntity.setUpdateDate(new Date());
        trkService.updateById(trkEntity);

        entity.setStatus(WcsConstants.RECEIVE_TASK_CANCEL);
        entity.setComments("指令取消成功-MES!");
        entity.setUpdateDate(new Date());
        baseDao.updateById(entity);

        logWorkService.workLogging(workName, dto.getRequestPk(), code, msg, "", workProc, WcsConstants.MES_USER_NAME, 3L);
        return new ApiResult().feedBack(dto.getRequestPk());
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public void analyzeHandler(ReceiveTaskDTO dto) {
        String workName = "[流程-ANALYZE]指令解析";
        String workProc = "ReceiveTaskServiceImpl.analyzeHandler";
        String pCode = WcsConstants.PROC_CODE_SUCCESS;
        String pMsg;
        String contNo = dto.getContNo();
//        MES要供料->如果机台有料则与所要料的空满反着来。
        String isFull = dto.getIsFull();
        String dvcNo = "HK_AGV";
        String trkType = WcsConstants.TRK_TYPE_OT;
        //使用UUID生成groupNo
        String groupNo = UUID.randomUUID().toString().replaceAll("-", "");
        ReceiveTaskEntity receiveTaskEntity = ConvertUtils.sourceToTarget(dto, ReceiveTaskEntity.class);
        if (StringUtils.isBlank(contNo)) {
//            更新[wcs_receive_task]的状态
            pCode = "analyzeHandler_001";
            pMsg = "指令解析异常!料箱条码不能为空!";
            receiveTaskEntity.setStatus(WcsConstants.RECEIVE_TASK_ERROR);
            receiveTaskEntity.setComments(pCode + ":" + pMsg);
            receiveTaskEntity.setUpdateDate(new Date());
            baseDao.updateById(receiveTaskEntity);
//            记录日志
            logWorkService.workLogging(workName, contNo, pCode, pMsg, "", workProc, WcsConstants.WCS_USER_NAME, 10L);
            return;
        }
//        1.开始对任务进行解析(mes只有出库任务-出到各个机台)
        TrkEntity existsEntity = trkService.selectByContNo(contNo);
        if (existsEntity != null) {
            pCode = "analyzeHandler_002";
            pMsg = "指令解析异常!料箱[" + contNo + "]在[wcs_trk]表中已存在任务[" + existsEntity.getTrkId() + "]!";
            receiveTaskEntity.setStatus(WcsConstants.RECEIVE_TASK_ERROR);
            receiveTaskEntity.setComments(pCode + ":" + pMsg);
            receiveTaskEntity.setUpdateDate(new Date());
            baseDao.updateById(receiveTaskEntity);
//            记录日志
            logWorkService.workLogging(workName, contNo, pCode, pMsg, "", workProc, WcsConstants.WCS_USER_NAME, 0);
            return;
        }
//        如果是出库任务，找一个缓存货位中的库存status=2

        Instant start = Instant.now();
        TrkEntity trkEntity = ConvertUtils.sourceToTarget(dto, TrkEntity.class);
        //        如果是入库任务,找一下可用的缓存货位status=0
        if (WcsConstants.CACHE_POSITION.equals(dto.getToPos())) {
            StnMappingEntity usableStnByNo = stnMappingService.getUsableStnByNo(dto.getToPos(), 0);
            if (usableStnByNo == null) {
//                暂无可用缓存货位
                pCode = "analyzeHandler_003";
                pMsg = "指令解析异常!缓存区域[" + WcsConstants.CACHE_POSITION + "]暂无空闲库位!";
                receiveTaskEntity.setComments(pCode + ":" + pMsg);
                receiveTaskEntity.setUpdateDate(new Date());
                baseDao.updateById(receiveTaskEntity);
//            记录日志
//                logWorkService.workLogging(workName, contNo, pCode, pMsg, "", workProc, WcsConstants.WCS_USER_NAME, 0);
                return;
            }
//            找到缓存货位后更新[wcs_stn_mapping]表
            usableStnByNo.setStatus(1);
            usableStnByNo.setContNo(contNo);
            usableStnByNo.setUpdateDate(new Date());
            stnMappingService.updateById(usableStnByNo);
            trkEntity.setToPos(usableStnByNo.getWcsStation());
        } else if (WcsConstants.CACHE_POSITION.equals(dto.getFrmPos())) {
//            从缓存区取出货物进行供料
            StnMappingEntity usableStock = stnMappingService.getUsableStnByType(dto.getContType(), isFull);
            if (usableStock == null) {
//                暂无可用缓存货位
                pCode = "analyzeHandler_004";
                pMsg = "指令解析异常!缓存区域[" + WcsConstants.CACHE_POSITION + "]暂无可用库存!类型[" + dto.getContType() + "]是否空料架[" + isFull + "]";
                receiveTaskEntity.setComments(pCode + ":" + pMsg);
                receiveTaskEntity.setUpdateDate(new Date());
                baseDao.updateById(receiveTaskEntity);
//            记录日志
//                logWorkService.workLogging(workName, contNo, pCode, pMsg, "", workProc, WcsConstants.WCS_USER_NAME, 0);
                return;
            }
//            锁定相关料架库存(预分配)
            usableStock.setStatus(1);
            usableStock.setComments("向机台供应料架!关联任务[" + dto.getRequestPk() + "]");
            usableStock.setUpdateDate(new Date());
            stnMappingService.updateById(usableStock);
            trkEntity.setFrmPos(usableStock.getWcsStation());
//            判断目标机台是否存在满料货架,如果存在则优先生成一条回库的任务
            StnMappingEntity machineStn = stnMappingService.getUsableStnByNo(dto.getToPos(), 2);
            if (machineStn != null) {
//                匹配一个空闲的缓存货位将满料架先接回缓存区
                StnMappingEntity usableStn = stnMappingService.getUsableStnByNo(WcsConstants.CACHE_POSITION, 0);
                if (usableStn == null) {
//                暂无可用缓存货位
                    pCode = "analyzeHandler_005";
                    pMsg = "指令解析异常!缓存区域[" + WcsConstants.CACHE_POSITION + "]暂无空闲库位!";
                    receiveTaskEntity.setComments(pCode + ":" + pMsg);
                    receiveTaskEntity.setUpdateDate(new Date());
                    baseDao.updateById(receiveTaskEntity);
                    return;
                }
//                将可用的缓存货位锁定(状态预分配+料架+空满状态)
                usableStn.setStatus(1);
                usableStn.setContNo(machineStn.getContNo());
                usableStn.setContType(machineStn.getContType());
                usableStn.setIsFull(machineStn.getIsFull());
                usableStn.setComments("前置任务!优先将该机台料架下料");
                usableStn.setUpdateDate(new Date());
                stnMappingService.updateById(usableStn);
//                生成前置任务(真实任务暂缓搬送)
                dvcNo = "WAIT_AGV";
                TrkDTO preTrk = new TrkDTO();
                preTrk.setRequestPk(dto.getRequestPk() + "#mcs");
                preTrk.setContNo(machineStn.getContNo());
                preTrk.setDvcNo("HK_AGV");
                preTrk.setContType(machineStn.getContType());
                preTrk.setFrmPos(machineStn.getWcsStation());
                preTrk.setToPos(usableStn.getWcsStation());
                preTrk.setCurPos(machineStn.getWcsStation());
                preTrk.setTrkType(WcsConstants.TRK_TYPE_IN);
//                前置任务的空满与MES所需料架的空满相反
                preTrk.setIsFull("false".equals(isFull) ? "true" : "false");
                preTrk.setGroupNo(groupNo);
                preTrk.setCreator(WcsConstants.WCS_USER_ID);
                preTrk.setCreateDate(new Date());
                preTrk.setUpdater(WcsConstants.WCS_USER_ID);
                trkService.save(preTrk);
            }
        }
//        [wcs_trk]数据填充
        trkEntity.setTrkPrty(Integer.valueOf(dto.getTrkPrty()));
        trkEntity.setDvcNo(dvcNo);
        trkEntity.setTrkType(trkType);
        trkEntity.setCurPos(dto.getFrmPos());
        trkEntity.setGroupNo(groupNo);
        trkEntity.setCreator(WcsConstants.MES_USER_ID);
        trkEntity.setCreateDate(new Date());
        trkEntity.setUpdater(WcsConstants.MES_USER_ID);
//        3.数据写入[WCS_TRK]表(返回生成的任务ID)
        trkService.insert(trkEntity);
        Long trkId = trkEntity.getTrkId();
//        处理完成更新[wcs_receive_task]表数据
        pMsg = "任务解析完成![" + contNo + "]从[" + trkEntity.getFrmPos() + "]到[" + trkEntity.getToPos() + "]生成搬送任务[" + trkId + "]";
        receiveTaskEntity.setStatus(WcsConstants.RECEIVE_TASK_RUNNING);
        receiveTaskEntity.setComments(pCode + ":" + pMsg);
        receiveTaskEntity.setUpdateDate(new Date());
        baseDao.updateById(receiveTaskEntity);
//        计算耗时
        Instant end = Instant.now();
        Duration times = Duration.between(start, end);

//        4.日志记录
        logWorkService.workLogging(workName, dto.getContNo(), pCode, pMsg, "", workProc, "WCS系统", times.toMillis());
    }

    @Override
    public void updateReceiveTaskByContNo(String contNo) {
        QueryWrapper<ReceiveTaskEntity> qw = new QueryWrapper<>();
        qw.lambda().eq(ReceiveTaskEntity::getContNo, contNo)
                .eq(ReceiveTaskEntity::getStatus, 1)
                .last("limit 1");
        ReceiveTaskEntity receiveTaskEntity = baseDao.selectOne(qw);
        receiveTaskEntity.setStatus(2);
        receiveTaskEntity.setUpdateDate(new Date());
        baseDao.updateById(receiveTaskEntity);
//        转移数据至历史并删除表数据
        baseDao.backupReceiveTask(contNo);
        baseDao.deleteById(receiveTaskEntity);
    }
}